package xyz.lwm.lazycat.beans;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import xyz.lwm.lazycat.event.AppFinishEvent;
import xyz.lwm.lazycat.event.AppStartErrEvent;
import xyz.lwm.lazycat.event.AppStartEvent;
import xyz.lwm.lazycat.event.AppStopErrEvent;
import xyz.lwm.lazycat.event.AppStopEvent;
import xyz.lwm.lazycat.event.Events;
import xyz.lwm.lazycat.setting.Setting;
import xyz.lwm.lazycat.setting.SettingSetter;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.ThrowableUtil;

import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * bean context starter, core codes
 *
 * @author lwm
 */
@Slf4j
class BeanContextStarter {

    // bean ref manager, 用于获取 bean 实例
    private final BeanRefs beanRefs = new BeanRefs();

    // bean design registry customizer manager, 用于扫描 bean class
    private final BeanDesignRegistryCustomizers registryCustomizers = new BeanDesignRegistryCustomizers();

    // bean design predicate manager, 用于过滤 bean design
    private final BeanRefFilters beanRefFilters = new BeanRefFilters();

    // bean processor of bean post processor, 用于 bean 的处理、初始化、销毁等操作
    private final BeanProcessor beanProcessor = new BeanProcessor();

    // bean renamer manager, 用于重命名 bean
    private final BeanRenamers beanRenamers = new BeanRenamers();

    // app runner manager, 容器启动完成后执行所有的 app runner
    private final AppRunners appRunners = new AppRunners();

    // bean design registry, 用于获取 bean design, 也可以注册 bean design
    private final BeanDesignRegistryImpl registry = new BeanDesignRegistryImpl(beanRefs, beanProcessor, beanRenamers);

    // bean 容器对外接口, 用于获取 bean 实例
    @Getter
    private final Beans beans = new BeanContext(beanRefs);

    // setting
    private final Setting setting = Setting.get();

    // 内置 bean design registry customizer 和 bean post processor
    public BeanContextStarter() {
        // 内置 bean design registry customizer
        // startup loader
        registryCustomizers.add(new BeanDesignStartupLoader(registryCustomizers));
        // spi loader
        registryCustomizers.add(new BeanDesignSpiLoader(registryCustomizers));

        // 内置 bean post processor
        // bean post processor for injection
        beanProcessor.add(Indexes.EARLIEST, new BeanPostProcessorForInjection(registry, beanRefs, beans, setting));
        // bean post processor for setting
        beanProcessor.add(Indexes.LATEST, new BeanPostProcessorForSetting(registry));

        // 注册事件监听
        long[] startTime = new long[]{System.currentTimeMillis()};
        Events.subscribe(AppStartEvent.class, e -> {
            startTime[0] = e.getTimeMills();
            log.info("App start... at {}", e.getStartTimeStr());
        });
        Events.subscribe(AppFinishEvent.class, e ->
            log.info("App finish... at {}, during {} sec",
                    e.getFinishTimeStr(), e.getDurationSec(startTime[0])) );
        Events.subscribe(AppStartErrEvent.class, e ->
            log.error("App start error... \n" + ThrowableUtil.print(e.getThrowable())) );
        Events.subscribe(AppStopEvent.class, e -> log.info("App stop..."));
        Events.subscribe(AppStopErrEvent.class, e ->
            log.error("App stop error... \n" + ThrowableUtil.print(e.getThrowable())) );
    }

    public void addBeanDesignRegistryCustomizer(BeanDesignRegistryCustomizer registryCustomizer) {
        registryCustomizers.add(registryCustomizer);
    }

    public void addBeanRefFilter(BeanRefFilter beanRefFilter) {
        beanRefFilters.add(beanRefFilter);
    }

    public void addBeanRenamer(BeanRenamer renamer) {
        beanRenamers.add(renamer);
    }

    public void start() {
        try {
            Events.publish(new AppStartEvent());
            start0();
            Events.publish(new AppFinishEvent());
        } catch (Throwable e) {
            Events.publish(new AppStartErrEvent(e));
            stop();
            throw e;
        }
    }

    /**
     * <h2>容器启动</h2>
     * 启动流程:
     * <p>1、类扫描: 启动类配置扫描 -> spi 文件配置扫描 -> configuration扫描 -> BeanScan扫描 -> @import 扫描 (configuration扫描 和 BeanScan扫描的递归处理)</p>
     * <p>2、收集 functional 的 bean design:</p>
     * <ul>BeanPostProcessor 实例化, 并存入 bean processor 中;</ul>
     * <ul>SettingCustomizer 实例化, 并扩展处理, 用于自定义修改配置信息;</ul>
     * <ul>BeanDesignRegistryCustomizer 实例化, 并扩展处理, 用于自定义注册 bean design 等;</ul>
     * <p>3、beanProcessor 排序 (index 小的排前面)</p>
     * <p>4、BeansCustomizer 实例化, 并扩展处理, 用于自定义注册 bean design 或获取 bean 实例;
     * 为什么 BeansCustomizer 要在 beanProcessor 排序后才能处理呢?
     * 因为 beanProcessor 排序后, 在获取 bean 实例时会自动处理 beanPostProcessor, 这是声明周期的重要部分。</p>
     * <p>5、获取所有 bean design, 都进行 get bean 操作, 实际上就是为了 create bean 和 process bean, 完成 bean 的声明周期</p>
     * <p>6、所有 bean 完成后, 执行 BeanLifecycle::postAllConstruct</p>
     * <p>7、最后执行 AppRunner::run, 容器中所有的 AppRunner 对象均执行 run 方法</p>
     */
    private void start0() {
        // registry customizers 递归扫描类
        while (registryCustomizers.hasNext()) {
            registryCustomizers.next().customize(registry);
        }

        // 处理 condition, 过滤不符合条件的 bean design 和 bean ref
        registry.filterByCondition();

        //  collect functional, 收集所有 functional bean design
        Collection<BeanDesign<?>> designs = registry.getDesigns(design ->
                Functional.class.isAssignableFrom(design.getType())).values();

        // collect bean post processor 实例
        designs.stream().filter(design -> BeanPostProcessor.class.isAssignableFrom(design.getType()))
                .forEach(design -> It.of(design.getName()).let(beanRefs::get)
                        .let(BeanRef::getBean).let(bean -> (BeanPostProcessor) bean)
                        .also(bean -> beanProcessor.add(design.getOrder(), bean)));

        // collect setting customizer
        designs.stream().filter(design ->
                        SettingCustomizer.class.isAssignableFrom(design.getType()))
                .forEach(design ->
                        It.of(design.getName()).let(beanRefs::get).let(BeanRef::getBean)
                                .let(bean -> (SettingCustomizer) bean)
                                .also(bean -> bean.customize(setting, (SettingSetter) setting)));

        // bean design registry customizer
        designs.stream().filter(design ->
                        BeanDesignRegistryCustomizer.class.isAssignableFrom(design.getType()))
                .forEach(design ->
                        It.of(design.getName()).let(beanRefs::get).let(BeanRef::getBean)
                                .let(bean -> (BeanDesignRegistryCustomizer) bean)
                                .also(bean -> bean.customize(registry)));

        // 注意: BeanPostProcessor, SettingCustomizer, BeanDesignRegistryCustomizer, 均需要在 beanProcessor 排序前设置
        // 所以这几个对象无法做注入, 因为注入时是需要在 beanPostProcessor 排序之后进行, 但可以构造时依赖其他对象, 所有的注入都不行;
        // BeanPostProcessor, SettingCustomizer, BeanDesignRegistryCustomizer 是基础工具, 一般不需要注入其他对象;
        // BeanPostProcessor 可能需要, 可以通过 BeansCustomizer 设置 beans, 再其实际工作时再获取某个 bean 对象

        // bean post processor 排序, index 小的排前面
        beanProcessor.sort();

        // beans customizer 的处理必须在 bean processor 排序之后
        // beans 获取之后就可以获取 bean 实例了, 获取的过程中就需要执行 bean post processor
        designs.stream().filter(design ->
                        BeansCustomizer.class.isAssignableFrom(design.getType()))
                .forEach(design -> {
                    // beans customizer
                    It.of(design.getName()).let(beanRefs::get).let(BeanRef::getBean)
                            .let(bean -> (BeansCustomizer) bean)
                            .also(bean -> bean.customize(registry, beans));
                });

        // 收集 app runners (此处 get bean 也会按照 bean 生命周期处理)
        designs.stream().filter(design -> AppRunner.class.isAssignableFrom(design.getType()))
                .forEach(design -> It.of(design.getName()).let(beanRefs::get)
                        .let(BeanRef::getBean).let(bean -> (AppRunner) bean).also(appRunners::add));

        // 此处开始按照生命周期来处理 bean, bean 的实例化按照 order 顺序处理
        // 但 bean 的创建过程可能依赖其他 bean, 此时会优先创建依赖 bean, 无依赖时则按照下面的遍历顺序创建
        // 如果需要提前获取某个 bean 实例, 可以通过 BeansCustomizer 来使用 beans 提前获取对象
        List<?> beans = beanRefs.map(beanRefFilters::test, ref -> ref).values().stream()
                .sorted(Comparator.comparingInt(ref -> ref.getDesign().getOrder()))
                .map((BeanRef::getBean)).collect(Collectors.toList());

        // 所有 bean 初始化后执行 postAllConstruct
        beans.stream().filter(BeanLifecycle.class::isInstance)
                .map(BeanLifecycle.class::cast).forEach(BeanLifecycle::postAllConstruct);

        // 最后运行 app runner
        appRunners.run();

        // runtime for shutdownHook
        Runtime.getRuntime().addShutdownHook(new Thread(this::stop));
    }

    public void stop() {
        try {
            Events.publish(new AppStopEvent());
            // 执行销毁方法
            beanRefs.map(ref -> true, ref -> ref)
                    .values().stream().map(BeanRef::getBean)
                    .forEach(beanProcessor::preDestroy);
        } catch (Throwable e) {
            Events.publish(new AppStopErrEvent(e));
        }
    }

}
